import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:built_collection/built_collection.dart';
import 'package:learning_content_admin_pannel.core/auth/application/bloc/bloc.dart';
import 'package:learning_content_admin_pannel.core/core/domain/entities/app_navigator_transition_info.dart';
import 'package:learning_content_admin_pannel.core/core/domain/entities/page_models/app_navigator_page_model.dart';
import 'package:learning_content_admin_pannel.core/core/domain/entities/user.dart';
import 'package:learning_content_admin_pannel.core/core/domain/utils/with_previous_stream.dart';
import 'package:learning_content_admin_pannel.core/root_navigator/domain/use_cases/get_initial_data/use_case.dart';
import 'package:learning_content_admin_pannel.core/root_navigator/domain/use_cases/handle_page_addition_to_navigator/use_case.dart';
import 'package:learning_content_admin_pannel.core/root_navigator/domain/use_cases/handle_page_removal_from_navigator/use_case.dart';
import 'package:learning_content_admin_pannel.core/root_navigator/domain/use_cases/handle_pop/use_case.dart';
import 'package:learning_content_admin_pannel.core/root_navigator/domain/use_cases/handle_user_outer_update/use_case.dart';
import 'package:learning_content_admin_pannel.core/root_navigator/domain/use_cases/update_page_stack/use_case.dart';

part 'bloc_state.dart';

class RootNavigatorBloc extends Cubit<RootNavigatorBlocState> {
  RootNavigatorBloc({
    required AuthBloc authBloc,
    required GetInitialData getInitialData,
    required HandlePageAdditionToNavigator handlePageAdditionToNavigator,
    required HandlePageRemovalFromNavigator handlePageRemovalFromNavigator,
    required HandlePop handlePop,
    required HandleUserOuterUpdate handleUserOuterUpdate,
    required UpdatePageStack updatePageStack,
  })  : _handlePageAdditionToNavigator = handlePageAdditionToNavigator,
        _handlePageRemovalFromNavigator = handlePageRemovalFromNavigator,
        _handlePop = handlePop,
        _handleUserOuterUpdate = handleUserOuterUpdate,
        _updatePageStack = updatePageStack,
        super(_getInitialState(
          authBlocState: authBloc.state,
          getInitialData: getInitialData,
        )) {
    streamWithPrevious = stream.withPrevious;

    _authBlocSubscription = authBloc.stream.listen(_onAuthBlocStateChanged);
  }

  final HandlePageAdditionToNavigator _handlePageAdditionToNavigator;
  final HandlePageRemovalFromNavigator _handlePageRemovalFromNavigator;
  final HandlePop _handlePop;
  final HandleUserOuterUpdate _handleUserOuterUpdate;
  final UpdatePageStack _updatePageStack;

  late final Stream<(RootNavigatorBlocState, RootNavigatorBlocState?)> streamWithPrevious;

  late final StreamSubscription<AuthBlocState> _authBlocSubscription;

  static RootNavigatorBlocState _getInitialState({
    required AuthBlocState? authBlocState,
    required GetInitialData getInitialData,
  }) {
    final result = getInitialData();

    return RootNavigatorBlocState(
      pageModels: result.pageModels,
      pageModelToTransitionInfo: result.pageModelToTransitionInfo,
      user: authBlocState?.user,
    );
  }

  void _onAuthBlocStateChanged(AuthBlocState authBlocState) {
    if (authBlocState.user == state.user) {
      return;
    }

    final result = _handleUserOuterUpdate(
      pageModels: state.pageModels,
      pageModelToTransitionInfo: state.pageModelToTransitionInfo,
      user: authBlocState.user,
    );

    emit(state.copyWith(
      pageModels: () => result.pageModels,
      pageModelToTransitionInfo: () => result.pageModelToTransitionInfo,
      user: () => authBlocState.user,
    ));
  }

  void requestPopHandling() {
    final result = _handlePop(
      pageModels: state.pageModels,
      pageModelToTransitionInfo: state.pageModelToTransitionInfo,
      user: state.user,
    );

    emit(state.copyWith(
      pageModels: () => result.pageModels,
      pageModelToTransitionInfo: () => result.pageModelToTransitionInfo,
    ));
  }

  void requestPageStackUpdate({
    BuiltList<AppNavigatorPageModel>? pageModels,
    BuiltMap<AppNavigatorPageModel, AppNavigatorTransitionInfo>? pageModelToTransitionInfo,
    User? Function()? user,
  }) {
    final effectiveUser = user == null ? state.user : user();

    final result = _updatePageStack(
      pageModels: pageModels ?? state.pageModels,
      pageModelToTransitionInfo: pageModelToTransitionInfo ?? state.pageModelToTransitionInfo,
      user: effectiveUser,
    );

    emit(state.copyWith(
      pageModels: () => result.pageModels,
      pageModelToTransitionInfo: () => result.pageModelToTransitionInfo,
      user: () => effectiveUser,
    ));
  }

  void requestPageAdditionToNavigatorHandling({
    required AppNavigatorPageModel pageModel,
  }) {
    final result = _handlePageAdditionToNavigator(
      pageModel: pageModel,
      pageModels: state.pageModels,
      pageModelToTransitionInfo: state.pageModelToTransitionInfo,
      user: state.user,
    );

    emit(state.copyWith(
      pageModels: () => result.pageModels,
      pageModelToTransitionInfo: () => result.pageModelToTransitionInfo,
    ));
  }

  void requestPageRemovalFromNavigatorHandling({
    required AppNavigatorPageModel pageModel,
  }) {
    final result = _handlePageRemovalFromNavigator(
      pageModel: pageModel,
      pageModels: state.pageModels,
      pageModelToTransitionInfo: state.pageModelToTransitionInfo,
      user: state.user,
    );

    emit(state.copyWith(
      pageModels: () => result.pageModels,
      pageModelToTransitionInfo: () => result.pageModelToTransitionInfo,
    ));
  }

  @override
  Future<void> close() {
    _authBlocSubscription.cancel();
    return super.close();
  }
}
